
#include "MetalMaterial.h"
#include "HitRecord.h"
#include "Light.h"
#include "Point.h"
#include "Primitive.h"
#include "Ray.h"
#include "RenderContext.h"
#include "Scene.h"
#include "Vector.h"
#include "Math.h"
#include "Background.h"
#include <fstream>
#include <iostream>
using namespace std;

MetalMaterial::MetalMaterial(const Color& color, float exponent)
  :color(color), exponent(exponent)
{
	std::cout << "Metal color: " << color.r() << " " << color.g() << " " << color.b() << std::endl;
	non_color = Color(1, 1, 1) - color;
}

MetalMaterial::~MetalMaterial()
{
}

void MetalMaterial::shade(Color& result, const RenderContext& context,
                               const Ray& ray, const HitRecord& hit, const Color& atten, int depth) const
{
	//check attenuation and max depth recursion
	const Scene* scene = context.getScene();
	if(depth >= scene->getMaxRayDepth() || atten.maxComponent() <= scene->getMinAttenuation()) {
		result = Color(0, 0, 0);
		return;
    }
	const vector<Light*>& lights = scene->getLights();
	Point hitpos = ray.origin()+ray.direction()*hit.minT();
	Vector normal;
	Vector refl_normal;
	hit.getPrimitive()->normal(normal, context, hitpos, ray, hit);
	double costheta = Dot(normal, ray.direction());
	if (costheta > 0) {
		normal = -normal;
		refl_normal = normal;
	} else {
		costheta = -1 * costheta;
		refl_normal = -normal;
	}
	double k = 1 - costheta;
	double k2 = k*k;
	double k4 = k2*k2;
	double k5 = k4*k;
	Color R = (color + non_color * k5);
	Vector reflection_dir = ray.direction() + normal * 2 * costheta;
		
	Ray reflected_ray( ray.origin() + hit.minT() * ray.direction(), reflection_dir );
	HitRecord reflectionHit( DBL_MAX );
	const Object* world = scene->getObject();
	world->intersect( reflectionHit, context, reflected_ray );
	if(reflectionHit.getPrimitive()){
		// Ray hit something...
	    const Material* matl = reflectionHit.getMaterial();
	    matl->shade(result, context, reflected_ray, reflectionHit, atten * R, depth + 1);
	} else {
		if(depth + 1 < scene->getMaxRayDepth())
			scene->getBackground()->getBackgroundColor( result, context, reflected_ray );
		else
			result = Color(0, 0, 0);
	}
	
	//Handle the phong portion of the metal effect
	Color phong_effect(0, 0, 0);
	Light*const* begin = &lights[0];
	Light*const* end = &lights[0]+lights.size();
	while (begin != end) {
		Color light_color;
		Vector light_direction;
		double dist = (*begin++)->getLight(light_color, light_direction, context,
				hitpos);
		double cosphi = Dot(normal, light_direction);
		if(cosphi > 0) {
			//light is visible, now get the phong exponent
			Vector H = light_direction - ray.direction();
			H.normalize();
			double cos_alpha = Dot(H, normal);
			if(cos_alpha > 0) {
				double phong_term = pow(cos_alpha, this->exponent);
				phong_effect = phong_effect + light_color*phong_term;
			}
		}
	}
	result = result * color + phong_effect * color;

}
